#pragma once

#include<iostream>
#include<string>
#include<vector>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<sys/wait.h>
#include<stdlib.h>
#include<sstream>
#include<unordered_map>
#include<sys/stat.h>
#include<algorithm>
#include<sys/sendfile.h>
#include<fcntl.h>
#include"Util.hpp"
#include"Log.hpp"

#define OK 200
#define NOT_FOUND 404
#define BAD_REQUEST 400
#define SERVER_ERROR 500

#define WEB_ROOT "wwwroot"
#define HOME_PAGE "index.html"
#define HTTP_VERSION "HTTP/1.0"
#define LINE_END "\r\n"
#define PAGE_404 "404.html"


static std::string CodeToDesc(int code) {

    switch(code) {
        case 200:
            return "OK";
        case 404:
            return "Not Found";
        default:
            break;
    }
}

static std::string SuffixToDesc(const std::string& suffix) {

    static std::unordered_map<std::string, std::string> suffix_desc = {
        {".html", "text/html"},
        {".css", "text/css"},
        {".js", "application/javascript"},
        {".jpg", "application/x-jpg"},
        {".xml", "application/xml"}
    };

    auto iter = suffix_desc.find(suffix);
    if (iter != suffix_desc.end()) {
        return suffix_desc[suffix];
    }
    return "text/html";
}

class HttpRequest{
public:
    //请求行
    std::string request_line_;
    //请求报头
    std::vector<std::string> request_header_;
    //空行
    std::string black_;
    //正文
    std::string request_body_;

    //将解析HTTP请求的内容也放到这个类里
    //请求行的内容解析
    std::string method_;
    std::string uri_;   //已经验证过，GET请求可能还会传递参数
    std::string version_;

    //将uri拆分
    std::string path_;
    std::string parameter_;

    //请求报头的内容解析
    std::unordered_map<std::string, std::string> header_kv_;

    //请求正文的内容解析
    int content_length_;

    //请求资源路径的后缀（判断文件类型）
    std::string suffix_;

    //是否需要用到CGI机制
    bool cgi_ = false;

    //请求资源的大小
    int size_;
};

class HttpResponse {
public:
    HttpResponse(int status_code = OK):status_code_(status_code), black_(LINE_END), fd_(-1) {}
    //状态行
    std::string status_line_;
    //响应报头
    std::vector<std::string> response_header_;
    //空行
    std::string black_;
    //正文
    std::string response_body_;

    int status_code_;

    //文件描述符（响应正文的内容）
    int fd_;
};

//处理（读取请求，分析请求，构建响应）
class EndPoint {
public:
    EndPoint(int sock):sock_(sock), stop_(false) {
    }

    bool Stop() {
        return stop_;
    }

    //读取请求
    bool RcvHttpRequest() {

        if (this->RcvHttpRequestLine() || this->RcvHttpRequestHeader()) {

        }
        else {
            //处理请求
            this->ParseHttpRequestLine();
            this->ParseHttpRequestHeader();
            this->RcvHttpRequestBody();
        }
        //要先解析完请求头再接受RequestBody
        //this->RcvHttpRequestBody();
        return stop_;
    }

    //构建响应
    void BuildHttpResponse() {

        //再判断路径是否是合法的
        struct stat st;
        //记录资源的大小，HTTP响应报文会用到
        int size = 0;       //变量定义放前面，因为goto语句可能会跳过
        int found = 0;      //确定文件类型（即文件名中.后面的内容）

        int& status_code = http_response_.status_code_;
        if (http_request_.method_ != "GET" && http_request_.method_ != "POST") {
            //非法请求
            LOG(WARNING, "请求方法错误！");
            status_code =  BAD_REQUEST;

            goto END;
        }
        if (http_request_.method_ == "GET") {
            ssize_t pos = http_request_.uri_.find('?');
            if (pos != std::string::npos) {
                //将路径和参数拆分
                Util::CutString(http_request_.uri_, http_request_.path_, http_request_.parameter_, "?");
                //有参数，可能需要对数据进行处理
                http_request_.cgi_ = true;
            }
            else {
                http_request_.path_ = http_request_.uri_;
            }
        }
        else if (http_request_.method_ == "POST") {
            http_request_.cgi_ = true;
            http_request_.path_ = http_request_.uri_;
        }
        //在请求路径前要加上网站的根目录
        http_request_.path_.insert(0, WEB_ROOT);
        if (http_request_.path_.back() == '/') {
            //同时，如果请求的资源不确定，就返回当前目录下的首页
            http_request_.path_.append(HOME_PAGE);
        }
        // //再判断路径是否是合法的
        // struct stat st;
        // //记录资源的大小，HTTP响应报文会用到
        // int size = 0;
        if (stat(http_request_.path_.c_str(), &st) == 0) {
            //说明资源是存在的
            //接下来判断资源是否合法

            //1.是否为目录文件
            if (S_ISDIR(st.st_mode)) {
                //默认返回该目录下的首页
                http_request_.path_.push_back('/');
                http_request_.path_.append(HOME_PAGE);
                //如果是目录文件，更新资源状态
                stat(http_request_.path_.c_str(), &st);
            }
            //2.是否为可执行文件(拥有者，所属组或其他人)
            else if((st.st_mode&S_IXUSR) || (st.st_mode&S_IXGRP) || (st.st_mode&S_IXOTH)){
                //特殊处理
                http_request_.cgi_ = true;
            }
            size = st.st_size;
        }
        else {
            //说明资源不存在
            std::string info = http_request_.path_;
            info += " Not Found!";
            LOG(WARNING, info);
            status_code = NOT_FOUND;
            
            goto END;
        }

        found = http_request_.path_.rfind(".");
        if (found == std::string::npos) {
            http_request_.suffix_ = ".html";    //默认后缀
        }
        else {
            http_request_.suffix_ = http_request_.path_.substr(found);
        }

        if (http_request_.cgi_) {
            this->ProcessCgi();
        }
        else {
            http_request_.size_ = size;
            status_code = this->ProcessNoCgi();
        }

        // std::cout << "path:"<< http_request_.path_ << std::endl;
        // std::cout << "parameter: " << http_request_.parameter_ << std::endl;

END:    

        BuildHttpResponseHelper();

    }

    //发送响应
    void SendHttpResponse() {
        send(sock_, http_response_.status_line_.c_str(), http_response_.status_line_.size(), 0);
        for (const auto& iter : http_response_.response_header_) {
            send(sock_, iter.c_str(), iter.size(), 0);
        }
        send(sock_, http_response_.black_.c_str(), http_response_.black_.size(), 0);

        if (http_request_.cgi_) {
            auto& response_body = http_response_.response_body_;
            size_t size = 0;
            size_t total = 0;
            const char* start = response_body.c_str();
            // size = send(sock_, start + total, response_body.size() - total, 0);
            // std::cout << size << std::endl;
            while (total < response_body.size() && ((size = send(sock_, start + total, response_body.size() - total, 0)) > 0)) {
                total += size;
            }
        }
        else {
            sendfile(sock_, http_response_.fd_, nullptr, http_request_.size_);
            close(http_response_.fd_);
        }
    }

    ~EndPoint() {
        if (sock_ != -1) {
            close(sock_);
        }
    }
private:
    //接收请求行
    bool RcvHttpRequestLine() {
        int status = Util::ReadLine(sock_, http_request_.request_line_);
        if (status > 0) {
            //http_request_.request_line_.resize(http_request_.request_line_.size() - 1); (是在干嘛？)
            LOG(INFO, http_request_.request_line_);
        }
        else {
            stop_ = true;
        }
        return stop_;
    }
    
    //接收请求报头
    bool RcvHttpRequestHeader() {
        //读取请求报头
        std::string line;
        while(line != "\n") {
            line.clear();   //清空，防止重复
            if (Util::ReadLine(sock_, line) <= 0) {
                //读取出现问题
                stop_ = true;
                break;
            }
            //line.resize(line.size() - 1);     (同上)
            http_request_.request_header_.emplace_back(line);
            // LOG(INFO, line);
        }
        http_request_.black_ = LINE_END;
        return stop_;
    }

    bool IsExistHttpRequestBody() {
        //只有POST请求才可能会有正文
        if (http_request_.method_ == "POST") {
            auto iter = http_request_.header_kv_.find("Content-Length");

            if (iter != http_request_.header_kv_.end()) {
                http_request_.content_length_ = stoi(iter->second);
                return true;
            }
        }
        return false;
    }

    //接收请求正文
    bool RcvHttpRequestBody() {

        if (this->IsExistHttpRequestBody()) {
            int len = http_request_.content_length_;
            std::string& body = http_request_.request_body_;
            char ch = 0;
            while (len--) {
                ssize_t s = recv(sock_, &ch, 1, 0);
                if (s > 0) {
                    body.push_back(ch);
                }
                else {
                    //之后再考虑
                    stop_ = true;
                    break;
                }
            }
            //std::cout << body << std::endl;
        }
        return stop_;
    }

    //解析请求行
    void ParseHttpRequestLine() {
        std::stringstream ss(http_request_.request_line_);
        ss >> http_request_.method_ >> http_request_.uri_ >> http_request_.version_;
        std::string& method = http_request_.method_;
        transform(method.begin(), method.end(), method.begin(), ::toupper);

        LOG(INFO, http_request_.method_);
        LOG(INFO, http_request_.uri_);
        LOG(INFO, http_request_.version_);
    }

    //解析请求报头
    void ParseHttpRequestHeader() {
        
        std::string key;
        std::string val;

        for (const std::string& line : http_request_.request_header_) {

            if (Util::CutString(line, key, val, ":")) {
                http_request_.header_kv_.insert(std::make_pair(key, val));
            }
        }

        // for (const auto& kv : http_request_.header_kv_) {
        //     std::cout << kv.first << ":" << kv.second;
        // }
    }

    //CGI处理
    int ProcessCgi() {
        
        int code = OK;

        auto& bin = http_request_.path_;
        auto& parameter = http_request_.parameter_;     //GET
        auto& content_length = http_request_.content_length_;   //POST


        std::string parameter_env;
        std::string method_env;
        std::string content_length_env;

        //使用cgi函数处理参数，解析内容，并返回

        //我们让子进程去执行函数，那么就要从父进程拿到相应参数
        //同时父进程还要读取返回结果
        //所以我们站在父进程的角度创建两个管道，一个让父进程用来写，一个用来读
        int input[2];
        int output[2];
        if (pipe(input) < 0) {
            LOG(ERROR, "pipe input error!");
            code = SERVER_ERROR;
            return code;
        }
        if (pipe(output) < 0) {
            LOG(ERROR, "pipe output error!");
            code = SERVER_ERROR;
            return code;
        }

        pid_t pid = fork();
        if (pid == 0) {
            close(input[0]);
            close(output[1]);
            //处理:exec函数
            //但是这里有一个问题，程序替换之后，代码和数据也都被替换了，两个数组也是同样的
            //所以替换之后我们没办法找到，因此我们要把文件描述符的0和1转换成我们读和写的管道文件

            dup2(input[1], 1);
            dup2(output[0], 0);

            //我们已经导入了环境变量，但是如何判断是GET方法导入的环境变量，还是POST方法使用的管道呢
            //再添加一个环境变量
            method_env = "METHOD=";
            method_env += http_request_.method_;

            putenv((char*)method_env.c_str());

            if (http_request_.method_ == "GET") {
                //通过环境变量导入参数信息
                parameter_env = "PARAMETER=";
                parameter_env += http_request_.parameter_;

                putenv((char*)parameter_env.c_str());

            }
            else if (http_request_.method_ == "POST") {
                content_length_env = "CONTENT_LENGTH=";
                content_length_env += std::to_string(content_length);
                putenv((char*)content_length_env.c_str());
            }
            execl(bin.c_str(), bin.c_str(), nullptr);

            //子进程执行结束直接退出
            exit(0);
        }
        else if (pid < 0){
            http_response_.status_code_ = NOT_FOUND;
            LOG(ERROR, "fork failed!");
            code = 404;
            return NOT_FOUND;
        }
        else {
            close(input[1]);
            close(output[0]);

            if (http_request_.method_ == "POST") {
                int size = content_length;
                const char* start = http_request_.request_body_.c_str();
                size_t total = 0;          //写入总长度
                size_t len = 0;            //单次写入长度
                while (total < content_length && (len = write(output[1], start + total, size - total)) > 0) {

                    total += len;

                }
            }

            char c;
            while (read(input[0], &c, 1)) {
                //读取自从进程传递的信息
                http_response_.response_body_.push_back(c);
            }
            
            int status;
            pid_t ret = waitpid(pid, &status, 0);
            //判断是否等待成功
            if (ret == pid) {
                //子进程是否正常退出且退出码为0
                if (WIFEXITED(status)) {
                    if (WEXITSTATUS(status) == 0) {
                        code = OK;
                    }
                    else {
                        code = BAD_REQUEST;
                    }
                }
                else {
                    code = SERVER_ERROR;
                }
            }
            close(input[0]);
            close(output[1]);
        }

        return code;
    }
    //非CGI处理
    int ProcessNoCgi() {
        
        http_response_.fd_ = open(http_request_.path_.c_str(), O_RDONLY);
        if (http_response_.fd_ >= 0) {
            //构建HttpResponse报文
            //状态行，响应报头，空行
            //状态行包含协议版本，状态码，状态码描述
            // http_response_.status_line_.append(HTTP_VERSION);
            // http_response_.status_line_.push_back(' ');
            // http_response_.status_line_.append(std::to_string(http_response_.status_code_));
            // http_response_.status_line_.push_back(' ');
            // http_response_.status_line_.append(CodeToDesc(http_response_.status_code_));
            // http_response_.status_line_.append(LINE_END);
             
            // //响应报头
            // std::string content_length("Content-Length: ");
            // content_length.append(std::to_string(http_response_.body_size_));
            // content_length.append(LINE_END);
            // http_response_.response_header_.emplace_back(content_length);
            // //请求资源的类型，其实就是后缀，所以第一个问题，获得后缀
            // //第二个问题，建立资源的对照表（网上搜）
            // std::string content_type("Content-Type: ");
            // content_type.append(SuffixToDesc(http_request_.suffix_));
            // content_type.append(LINE_END);
            // http_response_.response_header_.emplace_back(content_type);

            //响应正文
            //通过请求的路径返回内容
            //正常情况下，我们要使用open, read, send,中间经过了内核的多次拷贝
            //读的时候，先从磁盘读到内核，再拷贝给用户缓冲区
            //发送的时候也是先从用户缓冲区发送给内核，内核在发送给网卡
            //使用sendfile直接在内核进行处理

            //close(http_response_.fd_);
            return OK;
        }
        return NOT_FOUND;
    }

    void HandlerError() {

        std::string page(WEB_ROOT);
        page.push_back('/');
        page.append(PAGE_404);
        http_request_.cgi_ = false;
        //返回对应404页面
        http_response_.fd_ = open(page.c_str(), O_RDONLY);
        if (http_response_.fd_ > 0) {
            struct stat st;
            stat(page.c_str(), &st);
            std::string line = "Content-Type: text/html";
            line += LINE_END;
            http_response_.response_header_.emplace_back(line);
            line = "Content-Length: ";
            line += std::to_string(st.st_size);
            line += LINE_END;
            http_response_.response_header_.emplace_back(line);
            http_request_.size_ = st.st_size;
        }
    }

    void BuildOkResponse() {

            std::string line = "Content-Type: ";
            line += SuffixToDesc(http_request_.suffix_);
            line += LINE_END;
            http_response_.response_header_.emplace_back(line);
            line = "Content-Length: ";
            if (!http_request_.cgi_) {
                line += std::to_string(http_request_.size_);
            }
            else {
                line += std::to_string(http_response_.response_body_.size());
            }
            line += LINE_END;
            http_response_.response_header_.emplace_back(line);

    }



    void BuildHttpResponseHelper() {
        int& code = http_response_.status_code_;
        //构建状态行
        auto& status_line = http_response_.status_line_;
        status_line = HTTP_VERSION;
        status_line.push_back(' ');
        status_line += std::to_string(code);
        status_line .push_back(' ');
        status_line += CodeToDesc(code);
        status_line += (LINE_END);

        //构建响应报头
        switch(code) {
            case OK:
                BuildOkResponse();
                break;
            case NOT_FOUND:
                HandlerError();
                break;
            case SERVER_ERROR:
                HandlerError();
                break;
            case BAD_REQUEST:
                HandlerError();
                break;
            default:
                break;
        }
    }

    int sock_;
    HttpRequest http_request_;
    HttpResponse http_response_;
    bool stop_;     //判断程序是否出现问题
};

//将Entrance改造成回调函数
class CallBack {
public:
    CallBack() {

    }

    void operator()(int sock) {
        this->HandlerRequest(sock);
    }

    void HandlerRequest(int sock) {
        LOG(INFO, "HandlerRequest begin!");

        //std::cout << "Get a new link: " << sock << " ..." << std::endl;

// #ifndef DEBUG
// #define DEBUG

//         char buf[2048];
//         recv(sock, buf, sizeof(buf), 0);
//         std::cout << "-------------------------- begin --------------------------" << std::endl;
//         std::cout << buf << std::endl;
//         std::cout << "--------------------------- end ---------------------------" << std::endl;


// #endif

        EndPoint* ep = new EndPoint(sock);
        
        ep->RcvHttpRequest();
        if (!ep->Stop()) {
            LOG(INFO, "Receive http request success!");
            ep->BuildHttpResponse();
            ep->SendHttpResponse();
        }
        else {
            LOG(WARNING, "receive http request error!");
        }

        delete ep;
        
        LOG(INFO, "HandlerRequest end!");
    }

    ~CallBack() {

    }

private:

};
